package rocky.mvcportlet.control;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.portlet.PortletFileUpload;
import org.apache.log4j.Logger;

import rocky.common.BeanUtil;
import rocky.portlet.BaseInForm;
import rocky.portlet.BaseOutForm;

public class BaseControl {
    final public static Logger LOG = Logger.getLogger("BaseControl");
    final static String ADD_METHOD_ULFILE = "addUploadFile";

    /**
     * Default initialization which is invoked in the first time loading of portlet.
     * 
     * @param request
     * @param response
     * @throws PortletException
     * @throws IOException
     */
    public BaseOutForm init(RenderRequest request, RenderResponse response) throws PortletException, IOException {
        LOG.debug("init render.START");

        LOG.debug("init render.END");
        return null;
    }

    /**
     * Default init for action submit.
     * Support multipart form.
     * @param request
     * @param reqMap All submitted data are stored in reqMap
     * @param response
     * @return
     * @throws PortletException
     * @throws IOException
     */
    public BaseOutForm init(ActionRequest request, Map<String, Object> reqMap, ActionResponse response) throws PortletException, IOException {
        LOG.debug("init action.START");

        LOG.debug("init action.END");
        return null;
    }

    /**
     * Get data from web request to form bean.
     * 
     * @param req
     * @param clazz
     * @return
     */
//    public static BaseForm getData(ActionRequest req, Class clazz) {
//        LOG.debug("getData.START:clazz=" + clazz);
//        Object bean = null;
//
//        try {
//            bean = clazz.newInstance();
//            // 1. Get all method setters
//            Map<String, Method> beanSetterMap = BeanUtil.getWriteMethodMap(bean);
//
//            // Scan all properties of the bean
//            Method method = null;
//            if (!PortletFileUpload.isMultipartContent(req)) { // Normal form data
//                Object value;
//                for (String propName : beanSetterMap.keySet()) {
//                    // get value from the request basing property
//                    value = req.getParameter(propName);
//                    if (value == null) {
//                        value = req.getAttribute(propName);
//                        LOG.debug("Get data by getAttribute");
//                    } else {
//                        LOG.debug("Get data by getParameter");
//                    }
//
//                    if (value != null) {
//                        method = beanSetterMap.get(propName);
//                        // Call method setter of property
//                        LOG.debug("Set " + propName + "=" + value);
//                        method.invoke(bean, value);
//                    }
//                }
//            } else { // Upload form data
//                Method addUploadMethod = null;
//                for (MethodDescriptor methodDesc : Introspector.getBeanInfo(clazz).getMethodDescriptors()) {
//                    if (ADD_METHOD_ULFILE.equals(methodDesc.getMethod().getName())) {
//                        // addUploadFile method
//                        addUploadMethod = methodDesc.getMethod();
//                        break;
//                    }
//
//                }
//
//                FileItemFactory fileItemFactory = new DiskFileItemFactory();
//                PortletFileUpload portletFU = new PortletFileUpload(fileItemFactory);
//                List items = portletFU.parseRequest(req);
//                FileItem fit;
//                for (Iterator it = items.iterator(); it.hasNext();) {
//                    fit = (FileItem) it.next();
//                    if (fit.isFormField()) { // Normal field
//                        String fieldName = fit.getFieldName();
//                        if (beanSetterMap.containsKey(fieldName)) { // the field name is matched with properties of
//                                                                    // bean
//                            method = beanSetterMap.get(fieldName);
//
//                            String value = fit.getString();
//                            // Call method setter of property
//                            LOG.debug("Set " + fieldName + "=" + value);
//                            method.invoke(bean, value);
//                        }
//                    } else {
//                        // invode method "addUpload" method of bean
//                        if (addUploadMethod != null) { // if the bean has method "addUploadFile"
//                            addUploadMethod.invoke(bean, fit);
//                        }
//                    }
//                }
//            }
//        } catch (Exception ex) {
//            LOG.error("Get data form web request to form bean", ex);
//        }
//        LOG.debug("getData.END");
//
//        return (BaseForm) bean;
//    }

    public static BaseInForm getData(Map<String, Object> mapReq, Class clazz) {
        LOG.debug("getData.START:clazz=" + clazz);
        Object bean = null;

        try {
            bean = clazz.newInstance();
            // 1. Get all method setters
            Map<String, Method> beanSetterMap = BeanUtil.getWriteMethodMap(bean);

            // Scan all properties of the bean
            Method method = null;
            Object value;

            for (String propName : beanSetterMap.keySet()) {
                // get value from the request basing property
                value = mapReq.get(propName);

                method = beanSetterMap.get(propName);
                // Call method setter of property
                LOG.debug("Set " + propName + "=" + value);
                
                if (value != null) {
                    method.invoke(bean, value);
                }
            }
        } catch (Exception ex) {
            LOG.error("Get data form web request to form bean", ex);
        }
        LOG.debug("getData.END");

        return (BaseInForm) bean;
    }

    public static Map<String, Object> getMapData(ActionRequest req) {
        Map<String, Object> result = new HashMap<String, Object>();
        LOG.debug("getMapData.START:");
        Object bean = null;

        if (!PortletFileUpload.isMultipartContent(req)) { // Normal form data
            String fieldName;
            for (Enumeration<String> keyEnum = req.getParameterNames(); keyEnum.hasMoreElements();) {
                fieldName = keyEnum.nextElement();
                result.put(fieldName, req.getParameter(fieldName));
            }
            return result;
        } else { // Upload form data
            FileItemFactory fileItemFactory = new DiskFileItemFactory();
            PortletFileUpload portletFU = new PortletFileUpload(fileItemFactory);
            List items;
            try {
                items = portletFU.parseRequest(req);
                FileItem fit;
                String fieldName;
                String value;
                for (Iterator it = items.iterator(); it.hasNext();) {
                    fit = (FileItem) it.next();
                    fieldName = fit.getFieldName();

                    if (fit.isFormField()) { // Normal field
                        value = fit.getString();
                        LOG.debug("Set " + fieldName + "=" + value);
                        result.put(fieldName, value);
                    } else {
                        result.put(fieldName, fit);
                    }
                }
            } catch (FileUploadException e) {
                LOG.error("Parsing the request", e);
            }

        }
        LOG.debug("getMapData.END");

        return result;
    }

}
